﻿#region Using directives

using System;
//using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Thinktecture.Tools.Web.Services.CodeGeneration;
using Thinktecture.Tools.Web.Services.CodeGeneration.Driver;

#endregion

namespace Thinktecture.Tools.Web.Services.CodeGeneration.Driver
{
	public class OptionsTranslator
	{
		StringBuilder errorBuilder = new StringBuilder();
		StringBuilder warningBuilder = new StringBuilder();

		public OptionsTranslator()
		{

		}

		// TODO: Make these delegates!
		public string Warnings
		{
			get
			{
				return warningBuilder.ToString();
			}
		}

		public string Errors
		{
			get
			{
				return errorBuilder.ToString();
			}
		}
		public static string GetUsage()
		{
			return Utilities.Utility.CommandLineArgumentsUsage( typeof( ContractFirstArguments ) );
		}

		public ContractFirstArguments GetArguments(string[] args)
		{
			ContractFirstArguments arguments = new ContractFirstArguments();
			// CSC 11/25/2004
			// added error handling
			if( false == Utilities.Utility.ParseCommandLineArguments(args, arguments, new Utilities.ErrorReporter(RecordError)))
			{
				throw new BadParametersException( this.Errors );
			}
			CheckArguments( arguments );
			return arguments;
		}

		readonly String[] SUPPORTED_LANGUAGES = { "cs", "vb" };
		public void CheckArguments( ContractFirstArguments args )
		{
			CheckWsdl( args );
			CheckLanguage( args );
			// CSC 11/16/2004
			// Make sure namespace was specified.
			// CSC 11/19/2004
			// default namespace to empty string
			CheckNamespace( args );
			// CSC 11/19/2004
			CheckFileNames( args );
			// CSC 11/26/004
			CheckEndpointConfiguration( args );
			// CSC 11/25/2004
			CheckConfigurationFileType( args );
			// CSC 02/27/2005
			CheckHelp( args );
            // BDS 12/22/2005
            CheckCollection(args);
            CheckEnableWsdl(args);
            //BDDA 29/09/2006
            CheckGenerateDataContract(args);
		}

		// CSC 11/26/2004
		private void CheckEndpointConfiguration( ContractFirstArguments args )
		{
			if( true == args.GenerateServerCode 
				&& true == args.EnableEndpointConfiguration )
			{
				// sorry, can't do that!
				args.EnableEndpointConfiguration = false;
				RecordWarning( "/endpointconfiguration (/ec) option ignored for service code generation" );
			}
		}
		// CSC 11/25/2004
		private void CheckConfigurationFileType( ContractFirstArguments args )
		{
			if( (ConfigurationFileType.none != args.ConfigurationType )
				&& (false == args.EnableEndpointConfiguration)
				&& ( false == args.EnableSoapMessagesAccess ) )
			{
				// configuration file type is only relevant for endpoint configuration 
				// or soap message access
				RecordWarning( "option /configurationtype (/ct) is only valid with the options /endpointconfiguration (/ec) or /messageaccess (/ma). Parameter ignored." );
			}

			if( ConfigurationFileType.none == args.ConfigurationType 
				&& (( true == args.EnableEndpointConfiguration ) 
				|| true == args.EnableSoapMessagesAccess) )
			{
				RecordWarning( "Option /endpointconfiguration (/ec) or /messageaccess (/ma) specified without /configurationtype (/ct), defaulting to web.config." );
			}
		}

		private void CheckNamespace( ContractFirstArguments args )
		{
			// CSC 11/19/2004
			// default namespace to empty string
			if( null == args.Namespace )
			{
				args.Namespace = "";
			}
		}

		private void CheckWsdl( ContractFirstArguments args )
		{
			if ( null == args.WsdlLocation
				|| 0 == args.WsdlLocation.Length )
			{
				throw new NoWsdlSourceException();
			}
		}
		private void CheckLanguage( ContractFirstArguments args )
		{
			// TODO: This would be way easier if I
			// defined an enum for the accepted languages
			bool found = false;
			if ( null == args.CodeLanguage )
			{
				// CSC 11/16/2004
				// set C# as default language
				args.CodeLanguage = "cs";
			}
			else
			{
				for ( int i = 0; i < SUPPORTED_LANGUAGES.Length; i++ )
				{
					if ( SUPPORTED_LANGUAGES[i] == args.CodeLanguage )
					{
						found = true;
					}
				}
				if ( false == found )
				{
					throw new BadCodeLanguageException( args.CodeLanguage );
				}
			}
		}

        // BDS 12/22/2005
        private void CheckCollection(ContractFirstArguments args)
        {
            if (args.GenericList && args.GenerateCollections)
            {
                // GenericList and GenerateCollections argument cannot be true in the same instance.
                throw new BadArrayConversionException();
            }
        }

        // BDS 12/22/2005
        private void CheckEnableWsdl(ContractFirstArguments args)
        {
            if (!args.GenerateServerCode && args.EnableWsdl)
            {
                // GenericList and GenerateCollections argument cannot be true in the same instance.
                RecordWarning("option /enablewsdl (/ew) is only valid with the option /server (/s). Parameter ignored.");
                args.EnableWsdl = false;
            }
        }

        private void CheckGenerateDataContract(ContractFirstArguments args)
        {
            if (args.DataContract)
            {
                if (args.DataContractOutput == null || args.DataContractOutput.Trim() == "")
                {
                    if (args.CodeLanguage == "vb")
                        args.DataContractOutput = "Output.vb";
                    else if (args.CodeLanguage == "cs")
                        args.DataContractOutput = "Output.cs";
                }
                else
                {
                    if(args.DataContractOutput.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 ||
                        args.DataContractOutput.IndexOfAny(Path.GetInvalidPathChars()) > -1)
                        throw new BadParametersException("Data contract output file is invalid.");
                }

                if (args.DataContractNamespace == null || args.DataContractNamespace.Trim() == "")
                {
                    throw new BadParametersException("Data contract namespace (dcnamespace/dcn) parameter is required.");
                }
                else
                {
                    Regex regex = new Regex(@"^(?:(?:((?![0-9_])[a-zA-Z0-9_]+)\.?)+)(?<!\.)$");
                    Match match = regex.Match(args.DataContractNamespace);
                    if (!match.Success)
                    {
                        throw new BadParametersException("Data contract namespace is invalid.");
                    }
                }
            }
        }
    
		public WebServicesCodeGenerationOptions GetOptions(ContractFirstArguments args)
		{
			WebServicesCodeGenerationOptions options = new WebServicesCodeGenerationOptions();
			options.CodeLanguage = args.CodeLanguage;
			options.ConfigurationFile = args.ConfigurationFile;
			options.EnableEndpointConfiguration = args.EnableEndpointConfiguration;
			options.EnableSoapMessagesAccess = args.EnableSoapMessagesAccess;
			options.EnableValidation = args.EnableValidation;
			// CSC 11/25/2004
			// GenerateClientSideCode switches between adding configuration parameters to
			// app.config (for clients) and web.config (for services)
			// Sometimes this doesn't really match because you may want to 
			// add a proxy (GenerateClientSideCode=true) to a web application (ConfigurationFileType.web) 
			options.GenerateClientSideCode = (args.ConfigurationType == ConfigurationFileType.app);
			options.GenerateCollections = args.GenerateCollections;
			options.GenerateProperties = args.GenerateProperties;
			options.GenerateSerializableTypes = args.GenerateSerializableTypes;
			// CSC 11/18/2004
			// WebServiceImplementation file really wants to code-behind file name
			// to stuff it codebehind parameter in the Page directive of the .asmx file
			// CSC 11/22/2004
			// Changed name generation again
			options.WebServiceImplementationFile = ResultFileWriter.AppendExtension( Path.GetFileNameWithoutExtension( args.WebServiceImplementationFile ), "asmx." + args.CodeLanguage );
			// CSC 11/20/2004
			// WsdlLocation in URL format for files and web locations
			options.WsdlLocation = GetLocationInUriFormat( args.WsdlLocation );
			
			// CSC 2/21/2005
			options.GenerateInterface = args.GenerateInterface;
			options.GenerateMultipleFiles = args.GenerateMultipleFiles;
			options.GenerateHelp = args.GenerateHelp;
            
            // BDS: 12/22/2005
            options.EnableWsdl = args.EnableWsdl;
            options.OrderIdentifiers = args.Ordered;
            options.Databinding = args.DataBinding;
            options.OldAsyncCode = args.OldAsync;
            options.NoXxxSpecifiedHandling = args.NoSpecifiedHandling;
            options.UseGenericList = args.GenericList;
            options.ChangeCasing = args.AdjustCasing;

            // CMCK 30/01/2009
		    options.RemoveDefaultValueAttribute = args.RemoveDefaultValueAttribute;
            
			return options;
		}

		private string GetLocationInUriFormat( string location )
		{
			string retVal;

			try
			{
				Uri url =  new Uri( location );
				retVal = url.ToString();
			}
			catch ( UriFormatException )
			{
				// treat location as a path
				retVal = new Uri( Path.GetFullPath(location) ).ToString();
			}
			return retVal;
		}

		public void CheckFileNames( ContractFirstArguments args )
		{
			// CSC 11/19/2004
			// check if we need to derive the filenames from the wsdl filename
			if( false == args.GenerateServerCode
				&& null == args.ProxyFileName )
			{
				args.ProxyFileName = ResultFileWriter.GetFileName(args.WsdlLocation, args.CodeLanguage.ToLower());
			}
			// CSC 11/24/2004
			// Allowed independent generation of proxy and service code
			if( true == args.GenerateServerCode )
			{
				if( null == args.ServiceStubFileName )
				{
					args.ServiceStubFileName = ResultFileWriter.GetFileName( args.WsdlLocation, args.CodeLanguage.ToLower());
				}
				if( null == args.WebServiceImplementationFile )
				{
					args.WebServiceImplementationFile = ResultFileWriter.GetFileName(args.WsdlLocation, "asmx." + args.CodeLanguage.ToLower());
				}
			}
		}

		private void CheckHelp( ContractFirstArguments args )
		{
			// help is a server-side feature, not a client feature
			if( true == args.GenerateHelp 
				&& false == args.GenerateServerCode )
			{
				RecordWarning( "Help is not a valid option for client side code generation. Ignoring /genhelp" );
				args.GenerateHelp = false;
			}
		}

		// TODO: Should be delegates rather than being implemented here
		private void RecordWarning( string warningMsg ) 
		{
			warningBuilder.Append( string.Format( "WARNING: {0}{1}", warningMsg, System.Environment.NewLine ) );
		}
		private void RecordError( string errorMsg ) 
		{
			errorBuilder.Append( string.Format( "ERROR: {0}{1}", errorMsg, System.Environment.NewLine )  );
		}
	}
}
